package com.cmbc.connect;

import java.util.HashMap;
import java.util.Map;

import org.budo.support.http.client.Request;
import org.budo.support.http.client.Response;
import org.budo.support.lang.util.StringUtil;
import org.springframework.util.Assert;

import com.alibaba.fastjson.JSONObject;

import lombok.ToString;
import lombok.extern.slf4j.Slf4j;

/**
 * @author lmw
 */
@ToString
@Slf4j
public class DBankMessageSender extends AbstractMessageSender {
    /**
     * @param merchantPrivateKeyPath 商户私钥
     * @param merchantPemPfxPin      秘钥pin
     * @param bankCertPath           银行公钥
     * @param url                    请求URL
     * @param merchantNo             商户号
     */
    public DBankMessageSender(String privateKeyPath, String pemPfxPin, String publicKeyPath, String url, String merchantNo) {
        Assert.hasText(privateKeyPath, "#33 privateKeyPath");
        Assert.hasText(pemPfxPin, "#34 pemPfxPin");
        Assert.hasText(publicKeyPath, "#35 publicKeyPath");
        Assert.hasText(url, "#36 url");
        Assert.hasText(merchantNo, "#37 merchantNo");

        this.setEncrypter(new SM2Encrypter(publicKeyPath, privateKeyPath, pemPfxPin));
        this.setUrl(url);
        this.setMerchantNo(merchantNo);
    }

    public String sendMessage(String sendTransCode, Map<String, Object> message, String channelJnlNo) {
        Assert.hasText(sendTransCode, "#45 sendTransCode");
        Assert.hasText(channelJnlNo, "#46 channelJnlNo");

        if (message == null) {
            log.error("#47 sendMessage, message=" + message);
            message = new HashMap<String, Object>();
        }

        // 添加头信息
        this.addHeader(message, channelJnlNo);

        // 添加交易码
        this.addSendTransCode(sendTransCode, message);

        // 加密
        String requestData = new JSONObject(message).toString();
        String signPassString = null;
        try {
            signPassString = this.getEncrypter().encryptAndSignMessage(requestData);
        } catch (Throwable e) {
            log.error("#60 " + e.getMessage(), e);
            throw new ConnectException("报文加密失败", e);
        }

        // 组装加密后准备发送给服务端的报文
        JSONObject jsonContent = new JSONObject();
        jsonContent.put(Constant.MerchantNo, this.getMerchantNo());
        jsonContent.put(Constant.SignFlag, "1");
        jsonContent.put(Constant.RequestData, "...");

        log.info("#69 sendMessage, jsonContent = " + jsonContent);

        jsonContent.put(Constant.RequestData, signPassString);

        // 发送
        String responseContent = null;
        String json = jsonContent.toString();

        Request request = new Request();
        request.setUrl(this.getUrl());
        request.setMethod(Request.POST);
        request.setRequestBody(json);

        try {
            Response response = request.execute();
            responseContent = response.getBody();
        } catch (Throwable e) {
            log.error("#87 request.execute error, e=" + e, e);
            throw new ConnectException("#90 request.execute error, url=" + this.getUrl(), e);
        }

        if (null == responseContent || responseContent.trim().isEmpty() || !responseContent.trim().startsWith("{") || !responseContent.trim().endsWith("}")) {
            log.error("#94 api request error, responseContent=" + responseContent + ", jsonContent=" + jsonContent + ", request=" + request);
            throw new RuntimeException("#95 api request error, " + responseContent);
        }

        // 解密报文
        String responseDecrypt = null;
        try {
            JSONObject responseJson = JSONObject.parseObject(responseContent);

            String content = responseJson.getString(Constant.Content);
            responseDecrypt = this.getEncrypter().decryptAndVerifyMessage(content);
        } catch (Throwable e) {
            log.error("#96 e=" + e + ", 原始报文信息=" + responseContent, e);
            throw new ConnectException("报文解密失败", e);
        }

        log.info("#117 requestData=" + StringUtil.cutLeft(requestData, 1024) + ", responseDecrypt=" + StringUtil.cutLeft(responseDecrypt, 1024));
        return responseDecrypt;
    }
}
